﻿// Klimov Ivan
// Saint Petersburg State University 
// 2013
open FCore.Numerics
open FCore.Numerics.GpuMath
open FCore.Numerics.GpuStat
open System.IO
open System.Diagnostics

let readMapInt (path:string) =
    let lines = File.ReadAllLines path
    lines.[0].Split() |> Array.map int

let readMapFloat (path:string) =
    let lines = File.ReadAllLines path
    lines.[0].Split() |> Array.map float

//initialization all scales
let scales = readMapInt "scales.txt"
let scaleCall = scales.[0]
let scaleLam = scales.[1]
let scaleVar = scales.[2]
let scaleExp = scaleLam + scaleVar
let scaleM = scaleLam/100 + 2

let pathToFun = "Fun.txt"
let pathToArg1 = "Arg1.txt"
let pathToArg2 = "Arg2.txt"
//
let initFun =  GpuMatrix([1;scaleCall], -1.0)
let initArg1 = GpuMatrix([1; scaleCall], -1.0)
let initArg2 = GpuMatrix([1; scaleCall], -1.0)

let initArrayGpu (gpuArray:GpuMatrix) (path:string) = 
    let arrayReading:float[] = path |> readMapFloat
    for i in 0 .. (arrayReading.Length - 1) do
        gpuArray.[i] <- arrayReading.[i]
    gpuArray

//Static matrix
let Fun  = initArrayGpu initFun pathToFun
let Arg1 = initArrayGpu initArg1 pathToArg1
let Arg2 = initArrayGpu initArg2 pathToArg2

//initialization Store on CPU
let initStore = Array2D.init scaleExp scaleM (fun i j -> if i < scaleVar 
                                                         then (if j < 1 
                                                               then 1.0 
                                                               else -1.0) 
                                                         else (if j < 1 
                                                               then 2.0 
                                                               else (if j = 1 
                                                                     then ((float i) - (float scaleVar) + 1.0) 
                                                                     else -1.0)))
//copy Store on GPGPU
let Store = GpuMatrix(initStore)

//add or no add element
let isAdd (rowStore:GpuMatrix) addingLamVar = 
    addingLamVar &!= rowStore       
   
let eigenCFA (store:GpuMatrix) = 
    let mutable change = true
    while change do
        change <- false
        printf "while\n"
        for i in 0 .. (scaleCall - 1) do
            let L = store.[(int Fun.[i])..(int Fun.[i]), 0..scaleM-1] 
            let L1 = store.[(int Arg1.[i])..(int Arg1.[i]), 0..scaleM-1]
            let L2 = store.[(int Arg2.[i])..(int Arg2.[i]), 0..scaleM-1]
            let forL = (int L.[0]) - 1
            for j in 1 .. forL do
                let v1 = ((int L.[j]) - 1)
                let v2 = ((int L.[j]) - 1 + scaleLam)
                for k in 1..((int L1.[0]) - 1) do
                    if (isAdd store.[v1..v1, 1..scaleM-1] L1.[k]) 
                    then (store.[v1, (int store.[v1,0])] <- L1.[k];
                        store.[v1, 0] <- 2.0;
                        change <- true)
                for k in 1..((int L2.[0]) - 1) do
                    if (isAdd store.[v2..v2, 1..scaleM-1] L2.[k])
                    then (store.[v2, (int store.[v2,0])] <- L2.[k];
                        store.[v2, 0] <- 2.0;
                        change <- true)
    store


let start = 
    let toMb = 1024*1024
    printf "Fun %f Mb" ((float scaleCall)/(float toMb) + 1.0)
    printf "Arg1 %f Mb" ((float scaleCall)/(float toMb) + 1.0)
    printf "Arg2 %f Mb" ((float scaleCall)/(float toMb) + 1.0)
    printf "Store %f Mb" ((float (scaleExp*scaleM))/(float toMb) + 1.0)
    System.Console.Read()
    let stopWatch = Stopwatch.StartNew()
    eigenCFA Store
    //printf "\nnew Store%A\n" (eigenCFA Store)

    stopWatch.Stop()
    printfn "%f" stopWatch.Elapsed.TotalMilliseconds

start